using System;
using System.IO;
using System.Threading;
using Core;
using Core.Extensions;
using Core.Hardware.I2C;
using Core.Hardware.RTC;
using Core.Hardware.Watchdog;
using EmbeddedWebserver.Core;
using MicroLiquidCrystal;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using Microsoft.SPOT.Net.NetworkInformation;
using SecretLabs.NETMF.Hardware.Netduino;
using SolarDevices;
using SolarDevices.Configuration;
using Lcd = Core.Hardware.LCD.Lcd;
using Watchdog = Core.Hardware.Watchdog.Watchdog;

namespace SolarWeb
{
    public class Monitor
    {
        private static Lcd _lcd;
        private Watchdog _watchdog;
        private Configuration _config;
        private InputPort _switch;
        private int _page;
        private long _lastTick;

#if DEBUG
        internal const string Root = @"\WINFS\";
        internal const string Log = @"\WINFS\Logs\log.txt";
        internal const string Events = @"\WINFS\Events";
        internal const string SolarConfigName = "SolarConfig.Debug.ini";
    #else
        internal const string Root = @"\SD\";
        internal const string Log = @"\SD\Logs\log.txt";
        internal const string Events = @"\SD\Events";
        internal const string SolarConfigName = "SolarConfig.ini";
    #endif
        private const int RefreshPeriod = 2000;
        private const int NUM_PAGES = 5;

        #region Initialization

        public void Initialize()
        {
            _watchdog = Watchdog.GetSingleton();
            _watchdog.RegisterTarget(Device.WebServer);
            var solarConfigFile = Path.Combine(Root, SolarConfigName);
            _config = new Configuration(solarConfigFile, Root);
            _config.ReadConfiguration();
            var logger = DebugLogger.GetLogger(Log);
            DS1307.SetNetduinoTimeFromRTC();
            InitNetwork(_config);
            EventLogger.GetLogger(Events);
#if I2C_LCD
            _switch = new InterruptPort(Pins.GPIO_PIN_D13, true, Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeLow);
            _switch.OnInterrupt += button_OnInterrupt;
#endif
        }

        void button_OnInterrupt(uint data1, uint data2, DateTime time)
        {
            if ((time.Ticks - _lastTick) > 2500000)
            {
                _lastTick = time.Ticks;
                if (_lcd.Backlight)
                {
                    var page = Page + 1;
                    Page = page % NUM_PAGES;
                }
                _lcd.Backlight = true;                
            }

        }

        private int Page
        {
            get
            {
                int rVal;
                lock (this)
                {
                    rVal = _page;
                }
                return rVal;
            }
            set
            {
                lock(this)
                {
                    _page = value;
                }
            }
        }

        public void Run()
        {
            int lastPage = 0;
            while (true)
            {
                try
                {
                    using (var solarSystem = new SolarSystem())
                    {
                        using (var webServer = new Webserver(_config.WebConfiguration))
                        {
                            webServer.RegisterThreadSafeHandler("events", new CalendarEventsHandler());
                            webServer.RegisterThreadSafeHandler("logs", new ChargeControllerLogHandler());
                            webServer.RegisterThreadSafeHandler("state", new StateHandler(solarSystem));
                            webServer.RegisterThreadSafeHandler("settings", new SettingsHandler());
                            webServer.RegisterThreadSafeHandler("scale", new ScaleHandler());
                            webServer.StartListening();
                            solarSystem.StartPolling();
                            _watchdog.StartCountdown();
                            var hasLCD = _config.SystemConfiguration.LCDEnabled;
                            while (true)
                            {
                                if (hasLCD)
                                {
                                    var cs = solarSystem.ReadChargeState();
                                    if (SetupLCD())
                                    {
                                        if (cs.Connected)
                                        {
#if I2C_LCD
                                            var currentPage = Page;
                                            if (currentPage != lastPage)
                                            {
                                                _lcd.Clear();
                                                lastPage = currentPage;
                                            }
                                            StringBuilder sb;
                                            switch (currentPage)
                                            {
                                                case 0:
                                                    sb = new StringBuilder();
                                                    sb.Append("PV ");
                                                    sb.Append(cs.PvVolts);
                                                    sb.Append("V ");
                                                    sb.Append(cs.PvAmps);
                                                    sb.Append("A");
                                                    _lcd.WriteLine(0, sb.ToString());

                                                    sb = new StringBuilder();
                                                    sb.Append("Bat ");
                                                    sb.Append(cs.BatteryVolts);
                                                    sb.Append("V ");
                                                    sb.Append(cs.BatteryAmps);
                                                    sb.Append("A");
                                                    _lcd.WriteLine(1, sb.ToString());

                                                    sb = new StringBuilder();
                                                    sb.Append(cs.EnergyToday);
                                                    sb.Append("kWh ");
                                                    sb.Append(cs.Watts);
                                                    sb.Append("W");
                                                    _lcd.WriteLine(2, sb.ToString());

                                                    sb = new StringBuilder();
                                                    var state = cs.State.Substring(1, cs.State.IndexOf(')') - 1);
                                                    sb.Append(state);
                                                    _lcd.WriteLine(3, sb.ToString());
                                                    break;
                                                case 1:
                                                    _lcd.WriteLine(0, "Generator");
                                                    sb = new StringBuilder();
                                                    if (cs.GeneratorPower == null)
                                                    {
                                                        sb.Append("Off");
                                                    }
                                                    else
                                                    {
                                                        sb.Append(cs.GeneratorPower);
                                                        sb.Append("W");
                                                    }
                                                    _lcd.WriteLine(2, sb.ToString());
                                                    break;
                                                case 2:
                                                    _lcd.WriteLine(0, "Consumption");
                                                    sb = new StringBuilder();
                                                    if (cs.InverterPower == null)
                                                    {
                                                        sb.Append("Off");
                                                    }
                                                    else
                                                    {
                                                        sb.Append(cs.InverterPower);
                                                        sb.Append("W");
                                                    }
                                                    _lcd.WriteLine(2, sb.ToString());
                                                    break;
                                                case 3:
                                                    _lcd.WriteLine(0, "Inverter Errors");
                                                    _lcd.WriteLine(2, cs.InverterError ?? "Inverter Ok");
                                                    break;
                                                case 4:
                                                    _lcd.WriteLine(0, "Controller Errors");
                                                    _lcd.WriteLine(2, cs.ControllerError ?? "Controller Ok");
                                                    break;
                                                default:
                                                    sb = new StringBuilder();
                                                    sb.Append("Page ");
                                                    sb.Append(Page.ToString("N"));
                                                    _lcd.WriteLine(0, sb.ToString());
                                                    break;
                                            }

#else
                                            var sb = new StringBuilder(24);
                                            sb.Append("PV ");
                                            sb.Append(cs.PvAmps);
                                            sb.Append("A Bat ");
                                            sb.Append(cs.BatteryVolts);
                                            sb.Append("V ");
                                            sb.Append(cs.BatteryAmps);
                                            sb.Append("A");
                                            _lcd.WriteLine(0, sb.ToString());

                                            sb = new StringBuilder(24);
                                            sb.Append(cs.EnergyToday);
                                            sb.Append("kWh ");
                                            sb.Append(cs.Watts);
                                            sb.Append("W - ");
                                            var state = cs.State.Substring(1, cs.State.IndexOf(')') - 1);
                                            sb.Append(state);
                                            _lcd.WriteLine(1, sb.ToString());
#endif
                                        }
                                        else
                                        {
                                            _lcd.Clear();
                                            _lcd.WriteLine(0, "No connection");
                                        }
                                    }
                                }
                                Thread.Sleep(RefreshPeriod);
                                if (webServer.IsAlive)
                                {
                                    _watchdog.CallIn(Device.WebServer);
                                }
                                else
                                {
                                    DebugLogger.TryLog("Webserver is not alive, forcing reset.");
                                    _watchdog.ForceReset();
                                }
                            }
                        }

                    }
                }
                catch (Exception ex)
                {
                    DebugLogger.TryLog(ex);
                }
            }
        }
        private bool SetupLCD()
        {
            if (_lcd == null)
            {
                try
                {
#if I2C_LCD
                    var shifter = new BaseShifterLcdTransferProvider.ShifterSetup
                    {
                        RW = ShifterPin.GP1, // not used
                        RS = ShifterPin.GP0,
                        Enable = ShifterPin.GP2,
                        D4 = ShifterPin.GP4,
                        D5 = ShifterPin.GP5,
                        D6 = ShifterPin.GP6,
                        D7 = ShifterPin.GP7,
                        BL = ShifterPin.GP3,
                    };
                    _lcd = new Lcd(new MCP23008LcdTransferProvider(I2CBusExtension.GetSingleton(), 0x27, shifter));
                    _lcd.Initialize(20, 4);
                    _lcd.BackLightDuration = 30000;
                    _lcd.Backlight = true;
#else
                    _lcd = new Lcd(new GpioLcdTransferProvider(Pins.GPIO_PIN_D13 // rs
                                                               , Pins.GPIO_PIN_D12 // enable 
                                                               , Pins.GPIO_PIN_D8 // d4
                                                               , Pins.GPIO_PIN_D9 // d5
                                                               , Pins.GPIO_PIN_D10 // d6
                                                               , Pins.GPIO_PIN_D11 // d7
                                          )
                                      , Pins.GPIO_NONE // Back light
                        );
                    _lcd.Initialize(24, 2);
#endif
                    _lcd.Clear();
                }
                catch (Exception)
                {
                    DebugLogger.TryLog("Failed to initialize LCD.");
                    _lcd = null;
                }
            }
            return _lcd != null;
        }

        public void InitNetwork(Configuration config)
        {
#if !DEBUG
            try
            {
                var networkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
                var dhcp = config.SystemConfiguration.DHCPEnabled;
                var deviceIp = config.SystemConfiguration.DeviceIp;
                var subnetMask = config.SystemConfiguration.SubnetMask;
                var gatewayIp = config.SystemConfiguration.GatewayIp;
                foreach (var networkInterface in networkInterfaces)
                {
                    if (networkInterface.NetworkInterfaceType == NetworkInterfaceType.Ethernet)
                    {
                        if (dhcp)
                        {
                            networkInterface.EnableDhcp();
                        }
                        else if (networkInterface.IPAddress != deviceIp || networkInterface.IsDhcpEnabled || networkInterface.SubnetMask != subnetMask || networkInterface.GatewayAddress != gatewayIp)
                        {
                            // Switch to Static ...
                            networkInterface.EnableStaticIP(deviceIp, subnetMask, gatewayIp);
                        }
                        break;
                    }
                }
                var nameService = NameService.GetSingleton();
                nameService.AddName(config.SystemConfiguration.NetBiosName, NameService.NameType.Unique, NameService.MsSuffix.Default);
            }
            catch (Exception ex)
            {
                DebugLogger.TryLog("Failed to initialize network: " + ex.Message);
            }

#endif
        }
        #endregion
    }
}
